﻿#region License
/*

File Upload HTTP module for ASP.Net (v 2.0)
Copyright (C) 2007-2008 Darren Johnstone (http://darrenjohnstone.net)

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace CoyoEden.Uploader
{
	/// <summary>
	/// Controller for uploads. Must be placed before all upload controls on the page.
	/// </summary>
	public class DJUploadController : WebControl
	{
		#region Member variables

		internal static string UPLOAD_ID_TAG = "::DJ_UPLOAD_ID::";
		internal static string UPLOAD_DEFAULT_PARAMETER_TAG = "::DJ_DEFAULT_UPLOAD_PARAMETER::";

		HiddenField _uploadID;
		HiddenField _parameters;
		string _scriptPath;
		string _cssPath;
		string _imagePath;
		UploadStatus _status;
		bool _showCancelButton;
		string _allowedFileExtentions="";
		bool _showProgressBar = true;
		bool _enableManualProcessing = true;
		IFileProcessor _processor;

		string DEFAULT_IMAGE_PATH = Utils.RelativeWebRoot+"assets/uploader/img";
		string DEFAULT_CSS_PATH = Utils.RelativeWebRoot + "assets/uploader/css";
		string DEFAULT_JS_PATH = Utils.RelativeWebRoot + "assets/uploader/js";

		#endregion

		#region Properties
		/// <summary>
		/// Gets/sets the default file processor.
		/// </summary>
		public IFileProcessor DefaultFileProcessor {
			get { return _processor; }
			set {
				_processor = value as IFileProcessor;

				if (_processor == null) {
					throw new ArgumentException("File processor must implement IFileProcessor");
				}
			}
		}

		/// <summary>
		/// Gets or sets the allowed file extensions (a comma separated list .pdf,.zip,.gif).
		/// </summary>
		/// <value>The allowed file extensions.</value>
		public string AllowedFileExtensions {
			get { return _allowedFileExtentions; }
			set { _allowedFileExtentions = value; }
		}

		/// <summary>
		/// Gets or sets the upload status.
		/// </summary>
		/// <value>The upload status.</value>
		public UploadStatus Status {
			get { return _status; }
			internal set { _status = value; }
		}

		/// <summary>
		/// Gets or sets the path to the script file.
		/// </summary>
		/// <value>The script path.</value>
		public string ScriptPath {
			get { return _scriptPath; }
			set { _scriptPath = value; }
		}

		/// <summary>
		/// Gets or sets the path to the css file.
		/// </summary>
		/// <value>The image path.</value>
		public string CSSPath {
			get { return _cssPath; }
			set { _cssPath = value; }
		}

		/// <summary>
		/// Gets or sets the image path.
		/// </summary>
		/// <value>The image path.</value>
		public string ImagePath {
			get { return _imagePath; }
			set { _imagePath = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether the cancel button should be shown.
		/// </summary>
		/// <value><c>true</c> if the cancel button should be shown; otherwise, <c>false</c>.</value>
		public bool ShowCancelButton {
			get { return _showCancelButton; }
			set { _showCancelButton = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether the progress bar should be shown.
		/// </summary>
		/// <value><c>true</c> if the progress bar should be shown; otherwise, <c>false</c>.</value>
		public bool ShowProgressBar {
			get { return _showProgressBar; }
			set { _showProgressBar = value; }
		}

		/// <summary>
		/// Gets or sets a value indicating whether manual processing should be allowed if the
		/// upload module is not installed.
		/// </summary>
		/// <value><c>true</c> if manual processing is allowed; otherwise, <c>false</c>.</value>
		public bool EnableManualProcessing {
			get { return _enableManualProcessing; }
			set { _enableManualProcessing = value; }
		}
		#endregion

		#region .ctor
		/// <summary>
		/// Initializes a new instance of the <see cref="DJUploadController"/> class.
		/// </summary>
		public DJUploadController() {
		}
		#endregion

		/// <summary>
		/// Raises the <see cref="E:System.Web.UI.Control.Init"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
		protected override void OnInit(EventArgs e) {
			base.OnInit(e);

			if (String.IsNullOrEmpty(ImagePath)) {
				ImagePath = DEFAULT_IMAGE_PATH;
			}

			ImagePath = ImagePath.TrimEnd('/') + "/";

			if (String.IsNullOrEmpty(CSSPath)) {
				CSSPath = DEFAULT_CSS_PATH;
			}

			CSSPath = CSSPath.TrimEnd('/') + "/";

			if (String.IsNullOrEmpty(ScriptPath)) {
				ScriptPath = DEFAULT_JS_PATH;
			}

			ScriptPath = ScriptPath.TrimEnd('/') + "/";

			if (UploadManager.Instance.ModuleInstalled) {
				_status = UploadManager.Instance.Status;
				UploadManager.Instance.Status = null;
			}
		}

		/// <summary>
		/// Raises the <see cref="E:System.Web.UI.Control.Load"/> event.
		/// </summary>
		/// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
		protected override void OnLoad(EventArgs e) {
			base.OnLoad(e);

			if (Page.IsPostBack && !UploadManager.Instance.ModuleInstalled && EnableManualProcessing) {
				ManualProcessUploads();
			}

			Utils.AddJavaScriptInclude(ScriptPath+"fileupload.js",Page,true);
			Utils.AddStylesheetInclude(CSSPath+"uploadstyles.css",Page,true);

			EnsureChildControls();

			StringBuilder initFileUploadJS = new StringBuilder();
			initFileUploadJS.Append("$(document).ready(");
			initFileUploadJS.Append("function(){");
			initFileUploadJS.AppendFormat("up_initFileUploads('{0}','{1}');", ImagePath, AllowedFileExtensions.ToLower());
			initFileUploadJS.AppendFormat("$('form').submit(");
			initFileUploadJS.Append("function(){");
			initFileUploadJS.AppendFormat("up_BeginUpload('{0}',{1});", _uploadID.ClientID, ShowCancelButton.ToString().ToLower());
			initFileUploadJS.Append("});");
			initFileUploadJS.Append("});");//endof $(document).ready

			_uploadID.Value = UPLOAD_ID_TAG + Guid.NewGuid().ToString();

			if (ShowProgressBar && UploadManager.Instance.ModuleInstalled) {
				Utils.AddJavaScriptText(initFileUploadJS.ToString(), Page);
			}
		}

		/// <summary>
		/// Gets the file processor associated with a control.
		/// </summary>
		/// <returns>The file processor or null if none is found.</returns>
		IFileProcessor GetProcessorForControl(Control c) {
			if (c.Parent.Parent.Parent is DJFileUpload) {
				return ((DJFileUpload)c.Parent.Parent.Parent).FileProcessor;
			} else {
				return null;
			}
		}

		/// <summary>
		/// Processes all upload controls in a control collection when the module is not installed.
		/// </summary>
		/// <param name="cc">Control collection.</param>
		/// <param name="proc">The default processor.</param>
		/// <param name="status">The upload status.</param>
		void ProcessUploadControls(ControlCollection cc, IFileProcessor defaultProcessor, UploadStatus status) {
			foreach (Control c in cc) {
				System.Web.UI.WebControls.FileUpload fu = c as System.Web.UI.WebControls.FileUpload;

				if (fu != null && fu.HasFile) {
					IFileProcessor controlProcessor = GetProcessorForControl(fu);
					IFileProcessor processor = controlProcessor == null ? defaultProcessor : controlProcessor;

					try {
						processor.StartNewFile(fu.FileName, fu.PostedFile.ContentType, null);
						processor.Write(fu.FileBytes, 0, fu.FileBytes.Length);
						processor.EndFile();

						status.UploadedFiles.Add(new UploadedFile(fu.FileName, processor.GetIdentifier(), null));
					} catch (Exception ex) {
						status.ErrorFiles.Add(new UploadedFile(fu.FileName, processor.GetIdentifier(), null, ex));
					}
				}

				if (c.HasControls()) {
					ProcessUploadControls(c.Controls, defaultProcessor, status);
				}
			}
		}

		/// <summary>
		/// Processes file uploads through the processor when the upload module is not installed.
		/// </summary>
		void ManualProcessUploads() {
			IFileProcessor processor;

			processor = _processor == null ? UploadManager.Instance.GetProcessor() : _processor;

			_status = new UploadStatus(-1);

			if (processor != null) {
				ProcessUploadControls(Page.Controls, processor, _status);
			}
		}

		/// <summary>
		/// Called by the ASP.NET page framework to notify server controls that use composition-based implementation to create any child controls they contain in preparation for posting back or rendering.
		/// </summary>
		protected override void CreateChildControls() {
			base.CreateChildControls();

			_uploadID = new HiddenField();
			Controls.Add(_uploadID);

			// Create the parameter field
			_parameters = new HiddenField();
			Controls.Add(_parameters);

			//progress div
			Controls.Add(new LiteralControl("<div id=\"upProgressWrapper\"></div>"));

		}

		/// <summary>
		/// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
		/// </summary>
		/// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
		protected override void OnPreRender(EventArgs e) {
			base.OnPreRender(e);

			if (_processor != null) {
				_parameters.Value = UPLOAD_DEFAULT_PARAMETER_TAG + UploadManager.Instance.SerializeProcessor(_processor);
			}
		}
	}
}
